/*
* Licensed under the MIT License
* 
* Copyright (c) 2010 Kay Kreuning
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* 
* http://code.google.com/p/as3hyvesapi
*/
package nl.hyves.api
{
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.net.URLRequest;
	
	import nl.hyves.api.errors.HyvesError;
	import nl.hyves.api.events.HyvesErrorEvent;
	import nl.hyves.api.events.HyvesServiceEvent;
	import nl.hyves.api.oauth.IAuthorizer;
	import nl.hyves.api.oauth.IOAuth;
	import nl.hyves.api.oauth.OAuth;
	import nl.hyves.api.rpc.AsyncToken;
	import nl.hyves.api.rpc.IAsyncToken;
	import nl.hyves.api.rpc.xml.XMLDecoder;
	
	/**
	 * Dispatched when authorization is granted, effectively when the user has logged in.
	 * 
	 * @eventType nl.hyves.api.events.HyvesServiceEvent.AUTHORIZATION_GRANTED
	 */
	[Event(name="authorized", type="nl.hyves.api.events.HyvesServiceEvent")]
	
	/**
	 * Dispatched when an error occured with the API call/response.
	 * 
	 * @eventType nl.hyves.api.events.HyvesErrorEvent.API_ERROR
	 */
	[Event(name="apiError", type="nl.hyves.api.events.HyvesErrorEvent")]
	
	/**
	 * HyvesService provides an AS3 interface to the API of the hyves.nl platform.
	 * To use this library you need to be a registered Hyves API partner.
	 * 
	 * @see http://trac.hyves-api.nl/wiki/APIGettingStarted
	 * 
	 * @author Kay Kreuning
	 */ 
	public class HyvesService extends EventDispatcher
	{
		private const ENDPOINT:String =						"http://data.hyves-api.nl/";
		private const HA_VERSION:String =					"2.0";
		private const HA_FORMAT:String =					"xml"
		private const HA_FANCYLAYOUT:String =				"false";
		private const HA_RESPONSECODE_ALWAYS_200:String =	"true";
		
		
		public function get authorizedUserId():String
		{
			return oauth.userId;
		}
		
		public function get authorizedMethods():Array
		{
			return oauth.methods;
		}
		
		public function get authorizationExpireDate():Date
		{
			return oauth.expireDate;
		}
		
		public function get isAuthorized():Boolean
		{
			return oauth.isAuthorized;
		}
		
		private var oauth:IOAuth;
		
		/**
		 * Creates a new <code>HyvesService</code> object.
		 * 
		 * @param consumerKey API consumer key
		 * @param consumerSecret API consumer secret
		 * @see http://www.hyves.nl/api/
		 */
		public function HyvesService(consumerKey:String, consumerSecret:String, authorizer:IAuthorizer)
		{
			var platformParameters:Object = {
				ha_version:					HA_VERSION,
				ha_format:					HA_FORMAT,
				ha_fancylayout:				HA_FANCYLAYOUT,
				ha_responsecode_always_200:	HA_RESPONSECODE_ALWAYS_200
			};
			
			oauth = new OAuth(consumerKey, consumerSecret, ENDPOINT, platformParameters);
			
			oauth.addEventListener(Event.COMPLETE, onAuthorized);
			
			oauth.authorize(authorizer);
		}
		
		/**
		 * Call an API method.
		 * 
		 * @param method API method to call
		 * @param parameters an <code>Object</code> with parameters to send to the method
		 * @return an <code>IAsyncToken</code> that handles the execution and status of the call
		 * @see http://trac.hyves-api.nl/wiki/APIMethods/2.0
		 */
		public function call(method:String, parameters:Object = null):IAsyncToken
		{
			// TODO: Check if authorized
			
			parameters ||= new Object();
			
			parameters["ha_method"] = method;
			
			var request:URLRequest = oauth.getURLRequest(parameters);
			
			var token:IAsyncToken = new AsyncToken(request, responseValidator, resultParser, faultParser);
			
			token.addEventListener(ErrorEvent.ERROR, onErrorEvent);
			
			return token;
		}
		
		/**
		 * Re-dispatches the OAuth complete event to clients know when the service is available.
		 * 
		 * @param event Received Event object
		 */
		private function onAuthorized(event:Event):void
		{
			dispatchEvent(new HyvesServiceEvent(HyvesServiceEvent.AUTHORIZED));
		}
		
		/**
		 * Re-dispatches api error events so the client can listen to the service globally.
		 * 
		 * @param event Received HyvesErrorEvent object
		 */
		private function onErrorEvent(event:ErrorEvent):void
		{
			var error:HyvesError = event.target.data;
			
			if (hasEventListener(HyvesErrorEvent.API_ERROR))
			{
			
				dispatchEvent(new HyvesErrorEvent(
						HyvesErrorEvent.API_ERROR,
						error.message,
						error.code,
						error.method
					)
				);
			}
		}
		
		/**
		 * Validates responses coming from the api.
		 * 
		 * @param response Received response
		 * @return true if the response is good, otherwise false
		 */
		private function responseValidator(response:String):Boolean
		{	
			return !(/<error_result>/).test(response);
		}
		
		/**
		 * Parses responses that are validated as good into Flash objects.
		 * 
		 * @param response Received response
		 * @return a Flash object
		 */
		private function resultParser(response:String):Object
		{
			return XMLDecoder.decodeXMLString(response);
		}
		
		/**
		 * Parses responses that are validated as bad into Flash objects.
		 * 
		 * @param response Received response
		 * @return a Flash object
		 */
		private function faultParser(response:String):Object
		{
			var xml:XML = new XML(response);
			
			return new HyvesError(
				xml.error_message.toString(),
				parseInt(xml.error_code.toString()),
				xml.request_parameters.parameter.(requestkey == "ha_method").requestvalue.toString()
			);
		}
	}
}